package com.ys.erp.stage_wfb_service.task;


import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.v3.ApiClient;
import com.alipay.v3.ApiException;
import com.alipay.v3.api.AlipayFundTransCommonApi;
import com.alipay.v3.api.AlipayFundTransUniApi;
import com.alipay.v3.model.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ys.erp.common.enums.EnableEnum;
import com.ys.erp.common.enums.LockEnum;
import com.ys.erp.common.enums.RedisKey;
import com.ys.erp.common.util.DateUtil;
import com.ys.erp.common.util.MoneyUtil;
import com.ys.erp.common.util.YsThread;
import com.ys.erp.entity.data.bean.*;
import com.ys.erp.entity.service.*;
import com.ys.erp.stage_wfb_service.config.ZfbConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * @author lwj
 * @date 2023/8/2
 */
@Service
@RequiredArgsConstructor
@Slf4j
@RefreshScope
public class WfbTask {

    public static final HashMap<Integer, BigDecimal> HUABEI_INTEREST_MAP = new HashMap<>();
    static {
        HUABEI_INTEREST_MAP.put(3, new BigDecimal("0.018"));
        HUABEI_INTEREST_MAP.put(6, new BigDecimal("0.045"));
        HUABEI_INTEREST_MAP.put(12, new BigDecimal("0.075"));
    }

    private final StringRedisTemplate stringRedisTemplate;

    private final RedissonClient redisson;

    private final IZfbSeparateAccountOrderService iZfbSeparateAccountOrderService;

    private final IWfbOrderService iWfbOrderService;

    private final IUserCatService iUserCatService;

    private final IShopExtService iShopExtService;

    private final IShopService iShopService;

    private final IWfbOrderWaterService iWfbOrderWaterService;

    private final IHuaBeiOrderService iHuaBeiOrderService;

    @Value("${properties.zfb-biz-scene}")
    private String zfbBizScene;

    @Value("${properties.zfb-product-code}")
    private String zfbProductCode;

    @Value("${properties.hua-bei-interest-rate}")
    private String huaBeiInterestRate;

    private final IZfbApiRecordService iZfbApiRecordService;

    @Scheduled(cron = "*/5 * * * * ?")
    public void separateAccount() {
        log.info("开始进行支付宝转账");
        RLock lock = redisson.getLock(LockEnum.WFB_SEPARATE_ACCOUNTS_LOCK.getLockKey());
        boolean b = lock.tryLock();
        if (!b) {
            log.info("支付宝转账任务正在执行中");
            return;
        }
        log.info("拿到锁，开始处理");
        String task = stringRedisTemplate.opsForList().rightPop(RedisKey.Enums.SEPARATE_ACCOUNTS_QUEUE.getKey());
        if (ObjectUtils.isEmpty(task)) {
            log.info("当前无任务");
            return;
        }
        log.info("开始处理任务{}", task);
        ZfbSeparateAccountOrder.SeparateAccountDTO separateAccountDto = JSONObject.parseObject(task, ZfbSeparateAccountOrder.SeparateAccountDTO.class);
        try {
            //查询是否存在分账的历史，如果存在则不处理
            QueryWrapper<ZfbSeparateAccountOrder> zfbOrderQw = new QueryWrapper<>();
            zfbOrderQw.lambda().eq(ZfbSeparateAccountOrder::getSourceOrderNo, separateAccountDto.getOrderNo());
            zfbOrderQw.lambda().eq(ZfbSeparateAccountOrder::getSourceOrderType, separateAccountDto.getType());
            zfbOrderQw.lambda().eq(ZfbSeparateAccountOrder::getPeriod, separateAccountDto.getPeriod());
            long count = iZfbSeparateAccountOrderService.count(zfbOrderQw);
            if (count > 0) {
                log.info("异常：该笔订单已经分过账");
                //保存到失败表
                saveFail(separateAccountDto, "异常：该笔订单已经分过账", null, null, null);
                return;
            }
            if (ZfbSeparateAccountOrder.Enums.WFB.name().equals(separateAccountDto.getType())) {
                wfbSeparate(separateAccountDto);
            } else if (ZfbSeparateAccountOrder.Enums.HUA_BEI.name().equals(separateAccountDto.getType())) {
                huaBeiSeparate(separateAccountDto);
            }

        }catch (Exception e) {
            e.printStackTrace();
            log.error("支付宝转账异常");
            saveFail(separateAccountDto, "分账异常," + e.getMessage() + "," + Arrays.toString(e.getStackTrace()), null, null, null);
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    //处理分账
    private void disposeWfbSeparateAccount(ZfbSeparateAccountOrder.SeparateAccountDTO separateAccountDto, String signShopCode, Shop shop, ShopExt shopExt, Long money, String remark) {
        ApiClient apiClient = ZfbConfig.zfbClientMap.get(signShopCode);
        if (ObjectUtils.isEmpty(signShopCode) || ObjectUtils.isEmpty(apiClient)) {
            log.info("签约门店未开通支付宝转账，联系开发组");
            saveFail(separateAccountDto, "签约门店未开通支付宝转账，联系开发组",  shop.getName(), shopExt.getZfbSeparateRatio(), money);
            return;
        }
        //保存到分账表
        ZfbSeparateAccountOrder zfbOrder = new ZfbSeparateAccountOrder();
        zfbOrder.setOrderNo(ZfbSeparateAccountOrder.getRandOrderNo());
        zfbOrder.setSourceOrderNo(separateAccountDto.getOrderNo());
        zfbOrder.setPeriod(separateAccountDto.getPeriod());
        zfbOrder.setAmount(separateAccountDto.getAmount());
        zfbOrder.setSeparateAmount(money);
        zfbOrder.setSourceOrderType(separateAccountDto.getType());
        zfbOrder.setShopCode(shopExt.getShopCode());
        zfbOrder.setShopName(shop.getName());
        zfbOrder.setSeparateRatio(shopExt.getZfbSeparateRatio());
        zfbOrder.setOriginZfbAccount(ZfbConfig.zfbOriginAccountMap.get(signShopCode));
        zfbOrder.setZfbAccount(shopExt.getZfbAccount());
        zfbOrder.setZfbName(shopExt.getZfbName());
        zfbOrder.setZfbBizScene(zfbBizScene);
        zfbOrder.setZfbProductCode(zfbProductCode);
        zfbOrder.setInitTime(LocalDateTime.now());
        //保存到转帐确认中队列
        String okIngStr = stringRedisTemplate.opsForValue().get(RedisKey.Enums.SEPARATE_ACCOUNTS_OK_ING.getKey());
        if (ObjectUtils.isEmpty(okIngStr)) {
            okIngStr = "[]";
        }
        List<ZfbSeparateAccountOrder> okIngOrderList = JSONArray.parseArray(okIngStr, ZfbSeparateAccountOrder.class);
        okIngOrderList.add(zfbOrder);
        stringRedisTemplate.opsForValue().set(
                RedisKey.Enums.SEPARATE_ACCOUNTS_OK_ING.getKey(),
                JSONArray.toJSONString(okIngOrderList)
        );
        //请求支付宝转账
        AlipayFundTransUniApi api = new AlipayFundTransUniApi(apiClient);
        AlipayFundTransUniTransferModel data = new AlipayFundTransUniTransferModel();
        // 设置商家侧唯一订单号
        data.setOutBizNo(zfbOrder.getOrderNo());
        // 设置订单总金额
        data.setTransAmount(MoneyUtil.long2Str(zfbOrder.getSeparateAmount()));
        // 设置描述特定的业务场景
        data.setBizScene(zfbBizScene);
        // 设置业务产品码
        data.setProductCode(zfbProductCode);
        // 设置业务备注
        data.setRemark(remark);
        // 设置转账业务的标题
        data.setOrderTitle("订单号：" + separateAccountDto.getOrderNo() + "，第" + separateAccountDto.getPeriod() + "期," + remark);
        // 设置收款方信息
        Participant payeeInfo = new Participant();
        payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
        payeeInfo.setIdentity(zfbOrder.getZfbAccount());
        payeeInfo.setName(zfbOrder.getZfbName());
        data.setPayeeInfo(payeeInfo);
        // 设置转账业务请求的扩展参数
        data.setBusinessParams("{}");
        try {
            AlipayFundTransUniTransferResponseModel response = api.transfer(data);
            YsThread.execute(() -> {
                saveApiRecord(data.toJson(), response.toJson());
            });
        } catch (ApiException e) {
            AlipayFundTransUniTransferDefaultResponse errorObject = (AlipayFundTransUniTransferDefaultResponse) e.getErrorObject();
            JSONObject eObj = JSONObject.parse(errorObject.toJson());
            YsThread.execute(() -> {
                //构建请求记录
                saveApiRecord(data.toJson(), errorObject.toJson());
            });
            saveFail(separateAccountDto, eObj.getString("message"), null, null, null);
        }
    }

    //构建请求记录
    private void saveApiRecord(String request, String response) {
        //构建请求记录
        ZfbApiRecord record = new ZfbApiRecord();
        record.setRecordType(ZfbApiRecord.Enums.SEPARATE_ACCOUNT.name());
        record.setRequestInfo(request);
        record.setResponseInfo(response);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        iZfbApiRecordService.save(record);
    }

    /**
     * 保存到失败表
     *
     * @param separateAccountDto                dto
     * @param failDes                           失败原因
     */
    private void saveFail(ZfbSeparateAccountOrder.SeparateAccountDTO separateAccountDto, String failDes,
                          String shopName,
                          String ratio,
                          Long  separateAmount) {
        //先移除转帐确认中的数据
        String okStr = stringRedisTemplate.opsForValue().get(RedisKey.Enums.SEPARATE_ACCOUNTS_OK_ING.getKey());
        if (ObjectUtils.isNotEmpty(okStr) && !"[]".equals(okStr)) {
            List<ZfbSeparateAccountOrder> zfbSeparateAccountOrders = JSONArray.parseArray(okStr, ZfbSeparateAccountOrder.class);
            zfbSeparateAccountOrders.removeIf(zfbOrder ->
                    zfbOrder.getSourceOrderNo().equals(separateAccountDto.getOrderNo()) &&
                            zfbOrder.getPeriod().equals(separateAccountDto.getPeriod())
            );
            stringRedisTemplate.opsForValue().set(
                    RedisKey.Enums.SEPARATE_ACCOUNTS_OK_ING.getKey(),
                    JSONArray.toJSONString(zfbSeparateAccountOrders)
            );
        }
        ZfbSeparateAccountOrder.SeparateAccountFailDTO dto = new ZfbSeparateAccountOrder.SeparateAccountFailDTO();
        dto.setOrderNo(separateAccountDto.getOrderNo());
        dto.setType(separateAccountDto.getType());
        dto.setPeriod(separateAccountDto.getPeriod());
        dto.setAmount(separateAccountDto.getAmount());
        dto.setShopName(shopName);
        dto.setRatio(ratio);
        dto.setFailDes(failDes);
        dto.setSeparateAmount(separateAmount);
        String failStr = stringRedisTemplate.opsForValue().get(RedisKey.Enums.SEPARATE_ACCOUNT_FAIL.getKey());
        if (ObjectUtils.isEmpty(failStr)) {
            failStr = "[]";
        }
        List<ZfbSeparateAccountOrder.SeparateAccountFailDTO> failList = JSONArray.parseArray(failStr, ZfbSeparateAccountOrder.SeparateAccountFailDTO.class);
        failList.add(dto);
        stringRedisTemplate.opsForValue().set(
                RedisKey.Enums.SEPARATE_ACCOUNT_FAIL.getKey(),
                JSONArray.toJSONString(failList)
        );
    }

    @Scheduled(cron = "*/3 * * * * ?")
    public void queryZfbOrderIsOk() {
        log.info("开始查询支付宝订单是否转账成功");
        RLock lock = redisson.getLock(LockEnum.WFB_SEPARATE_ACCOUNTS_QUERY_ORDER_LOCK.getLockKey());
        boolean b = lock.tryLock();
        if (!b) {
            log.info("有查询任务正在执行");
            return;
        }
        log.info("拿到锁，开始查询并处理");
        try {
            String okIngStr = stringRedisTemplate.opsForValue().get(RedisKey.Enums.SEPARATE_ACCOUNTS_OK_ING.getKey());
            if (ObjectUtils.isEmpty(okIngStr) || "[]".equals(okIngStr)) {
                log.info("无查询任务");
                return;
            }
            //转为数组
            List<ZfbSeparateAccountOrder> zfbSeparateAccountOrderList = JSONArray.parseArray(okIngStr, ZfbSeparateAccountOrder.class);
            log.info("查询到{}个订单，信息：{}", zfbSeparateAccountOrderList.size(),JSONObject.toJSONString(zfbSeparateAccountOrderList));
            Set<String> successSet = new HashSet<>();
            CompletableFuture<Void> execute = YsThread.execute(zfbSeparateAccountOrderList, zfbOrder -> {
                ApiClient apiClient = ZfbConfig.accountClientMap.get(zfbOrder.getOriginZfbAccount());
                log.info("开始处理订单{}", zfbOrder.getOrderNo());
                //查询支付宝
                // 构造请求参数以调用接口
                AlipayFundTransCommonApi api = new AlipayFundTransCommonApi(apiClient);
                // 设置商户转账唯一订单号
                String productCode = zfbProductCode;
                String outBizNo = zfbOrder.getOrderNo();
                String bizScene = zfbBizScene;
                AlipayFundTransCommonQueryResponseModel response = null;
                try {
                    response = api.query(productCode, bizScene, outBizNo, null, null);
                    log.info("{}接口请求正常{}", zfbOrder.getOrderNo(), response.toJson());
                    JSONObject resObj = JSONObject.parse(response.toJson());
                    if ("SUCCESS".equals(resObj.getString("status"))) {
                        //成功,进行新增操作
                        zfbOrder.setZfbOrderId(resObj.getString("order_id"));
                        zfbOrder.setZfbPayFundOrderId(resObj.getString("pay_fund_order_id"));
                        zfbOrder.setTransTime(DateUtil.stringToLocalDateTime(resObj.getString("pay_date")));
                        zfbOrder.setSeparateAmount(MoneyUtil.str2Long(resObj.getString("trans_amount")));
                        zfbOrder.setCreateTime(LocalDateTime.now());
                        zfbOrder.setUpdateTime(LocalDateTime.now());
                        iZfbSeparateAccountOrderService.save(zfbOrder);
                        successSet.add(zfbOrder.getOrderNo());
                    } else {
                        log.info("{},响应状态不是Success,{}", zfbOrder.getOrderNo(), response.toJson());
                    }
                } catch (ApiException e) {
                    AlipayFundTransCommonQueryDefaultResponse errorObject = (AlipayFundTransCommonQueryDefaultResponse) e.getErrorObject();
                    log.info("{}接口请求异常{}", zfbOrder.getOrderNo(), errorObject.toJson());
                }
            });
            execute.join();
            if (successSet.isEmpty()) {
                log.info("无任务成功");
                return;
            }
            zfbSeparateAccountOrderList.removeIf(zfbOrder -> successSet.contains(zfbOrder.getOrderNo()));
            stringRedisTemplate.opsForValue().set(RedisKey.Enums.SEPARATE_ACCOUNTS_OK_ING.getKey(),
                    JSONArray.toJSONString(zfbSeparateAccountOrderList));
            log.info("处理成功{}个订单,{}", successSet.size(), JSONObject.toJSONString(successSet));
        } catch (Exception e) {
            log.info("查询异常");
            e.printStackTrace();
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }

    }

    private void wfbSeparate(ZfbSeparateAccountOrder.SeparateAccountDTO separateAccountDto) {
        //正常分账,查询对应微分呗
        WfbOrder wfbOrder = iWfbOrderService.getByNo(separateAccountDto.getOrderNo());
        if (ObjectUtils.isEmpty(wfbOrder)) {
            saveFail(separateAccountDto, "异常：微分呗订单在系统中不存在", null, null, null);
            return;
        }
        if (ObjectUtils.isEmpty(wfbOrder.getOnlyUserNo())) {
            saveFail(separateAccountDto, "异常：该订单无绑定宠物信息，查找不到对应分账店铺", null, null, null);
            return;
        }
        //查询用户订单
        QueryWrapper<UserCat> userCatQw = new QueryWrapper<>();
        userCatQw.lambda().eq(UserCat::getCatCode, wfbOrder.getCatCode());
        userCatQw.lambda().eq(UserCat::getUserId, wfbOrder.getUserId());
        UserCat userCat = iUserCatService.getOne(userCatQw);
        if (ObjectUtils.isEmpty(userCat)) {
            saveFail(separateAccountDto, "异常：通过该微分呗查询不到宠物订单", null, null, null);
            return;
        }
        //计算分账金额
        QueryWrapper<WfbOrderWater> waterQw = new QueryWrapper<>();
        waterQw.lambda().eq(WfbOrderWater::getWfbSubscriptionNo, wfbOrder.getWfbSubscriptionNo());
        waterQw.lambda().eq(WfbOrderWater::getWfbPeriod, separateAccountDto.getPeriod());
        List<WfbOrderWater> waterList = iWfbOrderWaterService.list(waterQw);
        if (ObjectUtils.isEmpty(waterList) || waterList.size() != 1) {
            saveFail(separateAccountDto, "异常：微分呗订单流水异常",  null, null, null);
            return;
        }
        WfbOrderWater water = waterList.getFirst();
        separateSubmit(separateAccountDto, water.getWfbDeductionAmount(), new BigDecimal(0), userCat);
    }

    private void huaBeiSeparate(ZfbSeparateAccountOrder.SeparateAccountDTO separateAccountDto) {
        //正常分账,查询对应花呗
        HuaBeiOrder huaBeiOrder = iHuaBeiOrderService.getByNo(separateAccountDto.getOrderNo());
        if (ObjectUtils.isEmpty(huaBeiOrder)) {
            saveFail(separateAccountDto, "异常：花呗订单在系统中不存在", null, null, null);
            return;
        }
        if (ObjectUtils.isEmpty(huaBeiOrder.getOnlyUserNo())) {
            saveFail(separateAccountDto, "异常：该花呗订单无绑定宠物信息，查找不到对应分账店铺", null, null, null);
            return;
        }
        if (!HuaBeiOrder.Enums.TRADE_SUCCESS.name().equals(huaBeiOrder.getOrderStatus())) {
            saveFail(separateAccountDto, "异常：该花呗订单支付状态有误，请联系开发组", null, null, null);
            return;
        }
        //查询用户订单
        QueryWrapper<UserCat> userCatQw = new QueryWrapper<>();
        userCatQw.lambda().eq(UserCat::getCatCode, huaBeiOrder.getCatCode());
        userCatQw.lambda().eq(UserCat::getUserId, huaBeiOrder.getUserId());
        UserCat userCat = iUserCatService.getOne(userCatQw);
        if (ObjectUtils.isEmpty(userCat)) {
            saveFail(separateAccountDto, "异常：通过该花呗订单查询不到宠物订单", null, null, null);
            return;
        }
        //计算利息
        BigDecimal orderMoney = new BigDecimal(huaBeiOrder.getOrderMoney());
        BigDecimal interestRatio = HUABEI_INTEREST_MAP.get(huaBeiOrder.getPeriod());
        BigDecimal interest = orderMoney.multiply(interestRatio);
        separateSubmit(separateAccountDto, huaBeiOrder.getOrderMoney(), interest, userCat);
    }

    public void separateSubmit(ZfbSeparateAccountOrder.SeparateAccountDTO separateAccountDto, Long orderMoney, BigDecimal interest, UserCat userCat) {
        //查询领猫门店信息
        CompletableFuture<ShopExt> shopExtFuture = YsThread.execute(() -> {
            return iShopExtService.getByCode(userCat.getShopCode());
        });
        CompletableFuture<Shop> shopFuture = YsThread.execute(() -> {
            return iShopService.getByCode(userCat.getShopCode());
        });
        ShopExt shopExt = shopExtFuture.join();
        Shop shop = shopFuture.join();
        if (ObjectUtils.isEmpty(shopExt)) {
            saveFail(separateAccountDto, "异常：宠物领猫店铺" + userCat.getShopCode() + ", 店铺不存在", null, null, null);
            return;
        }
        //计算分账金额
        BigDecimal amount = new BigDecimal(orderMoney);
        BigDecimal zfbSeparateRatio = new BigDecimal(shopExt.getZfbSeparateRatio());
        if (zfbSeparateRatio.longValue() < 0) {
            zfbSeparateRatio = new BigDecimal("0");
        }
        BigDecimal moneyB = amount.multiply(zfbSeparateRatio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
        if (ObjectUtils.isEmpty(shopExt.getIsOpenZfbSeparateAccount()) || shopExt.getIsOpenZfbSeparateAccount() == EnableEnum.NO.getEnable()) {
            saveFail(separateAccountDto, "异常：店铺" + userCat.getShopCode() + ",未启用分账功能", shop.getName(), shopExt.getZfbSeparateRatio(), moneyB.longValue());
            return;
        }
        if (ObjectUtils.isEmpty(shopExt.getZfbName())) {
            saveFail(separateAccountDto, "异常：店铺" + userCat.getShopCode() + ",支付宝账户名称为空", shop.getName(), shopExt.getZfbSeparateRatio(), moneyB.longValue());
            return;
        }
        if (ObjectUtils.isEmpty(shopExt.getZfbAccount())) {
            saveFail(separateAccountDto, "异常：店铺" + userCat.getShopCode() + ",支付宝账号为空", shop.getName(), shopExt.getZfbSeparateRatio(), moneyB.longValue());
            return;
        }
        if (ObjectUtils.isEmpty(shopExt.getZfbSeparateRatio()) || Double.parseDouble(shopExt.getZfbSeparateRatio()) <= 0) {
            saveFail(separateAccountDto, "异常：店铺" + userCat.getShopCode() + ",支付宝分账比例为空", shop.getName(), shopExt.getZfbSeparateRatio(), moneyB.longValue());
            return;
        }
        //减去利息
        moneyB = moneyB.subtract(interest);
        //计算分账金额
        long money = moneyB.longValue();
        if (money < 100L) {
            saveFail(separateAccountDto, "异常：分账金额小于1",  shop.getName(), shopExt.getZfbSeparateRatio(), money);
            return;
        }
        if (separateAccountDto.getPeriod() == 1 && ZfbSeparateAccountOrder.Enums.WFB.name().equals(separateAccountDto.getType())) {
            if (money - 300 < 1) {
                saveFail(separateAccountDto, "异常：首期减去三元，分账金额小于1",  shop.getName(), shopExt.getZfbSeparateRatio(), money);
                return;
            }
            money -= 300;
        }
        String remark = userCat.getPeriodTitleDes();
        if (interest.longValue() > 0) {
            remark = remark + ", 利息:" + MoneyUtil.long2Str(interest.longValue()) + "元";
        }
        disposeWfbSeparateAccount(separateAccountDto, "hz0", shop, shopExt, money, remark);
    }

}
